﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Threading;
using System.Xml;
using System.IO;

namespace Prodcrast
{
    using Filter = Func<Episode, bool>;

    /// <summary>
    /// 
    /// </summary>
    public partial class ProdCrastMain : Form
    {
        List<Feed> m_Feeds = new List<Feed>();
        BindingSource m_FeedSource = new BindingSource();
        BindingSource m_EpisodeSource = new BindingSource();

        /// <summary>
        /// 
        /// </summary>
        public ProdCrastMain()
        {
            if (Properties.Settings.Default.DownloadDirectory == "")
            {
                Properties.Settings.Default.DownloadDirectory =
                    Path.Combine(
                        Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
                        "Podcasts"
                        );
            }

            InitializeComponent();

            SetupSystemMenu();

            ConstructFeedColumns();
            ConstructEpisodeColumns();

            tabAll.Tag = new Filter(
                delegate(Episode e) { return true; });
            tabListened.Tag = new Filter(
                delegate(Episode e) { return e.Listened; });
            tabNew.Tag = new Filter(
                delegate(Episode e) { return e.State == Episode.EpisodeState.Downloaded; });
            tabDownloading.Tag = new Filter(
                delegate(Episode e) 
                { 
                    return (e.State == Episode.EpisodeState.Downloading) ||
                        (e.State == Episode.EpisodeState.Queued) ||
                        (e.State == Episode.EpisodeState.Paused); 
                });
        
            FileInfo dataFile = new FileInfo("prodcrast.xml");
            if (dataFile.Exists)
            {
                LoadXMLData(dataFile.FullName);
            }

            m_FeedSource.DataSource = m_Feeds;
            dataGridFeeds.DataSource = m_FeedSource;

            dataGridEpisodes.DataSource = m_EpisodeSource;
        }

        /// <summary>
        /// 
        /// </summary>
        private void ScanAllFeedsAsync()
        {
            foreach (Feed f in m_Feeds)
            {
                Thread t = new Thread(f.Scan);
                t.Start();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void ConstructFeedColumns()
        {
            dataGridFeeds.AutoGenerateColumns = false;

            {
                DataGridViewColumn col = new DataGridViewColumn(new DataGridViewImageCell());
                dataGridFeeds.Columns.Add(col);
                col.DataPropertyName = "StateIcon";
                col.AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
                col.Width = 18;
                col.Resizable = DataGridViewTriState.False;
            }
            {
                DataGridViewColumn col = new DataGridViewColumn(new DataGridViewTextBoxCell());
                dataGridFeeds.Columns.Add(col);
                col.ReadOnly = true;
                col.DataPropertyName = "Name";
                col.HeaderText = "Name";
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void ConstructEpisodeColumns()
        {
            dataGridEpisodes.AutoGenerateColumns = false;

            {
                DataGridViewColumn col = new DataGridViewColumn(new DataGridViewImageCell());
                dataGridEpisodes.Columns.Add(col);
                col.DataPropertyName = "StateIcon";
                col.AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
                col.Width = 18;
                col.Resizable = DataGridViewTriState.False;
            }
            {
                DataGridViewColumn col = new DataGridViewColumn(new DataGridViewImageCell());
                dataGridEpisodes.Columns.Add(col);
                col.DataPropertyName = "Icon";
                col.AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
                col.Width = 18;
                col.Resizable = DataGridViewTriState.False;
            }
            {
                DataGridViewColumn col = new DataGridViewColumn(new DataGridViewTextBoxCell());
                dataGridEpisodes.Columns.Add(col);
                col.DataPropertyName = "Title";
                col.HeaderText = "Title";
                col.FillWeight = 250;
            }
            {
                DataGridViewColumn col = new DataGridViewColumn(new DataGridViewTextBoxCell());
                // dataGridEpisodes.Columns.Add(col);
                col.DataPropertyName = "Date";
                col.HeaderText = "Date";
            }
            {
                DataGridViewColumn col = new EpisodeGridViewColumn();
                dataGridEpisodes.Columns.Add(col);
                col.DataPropertyName = "This";
                col.HeaderText = "State";
            }

        }



        /// <summary>
        /// 
        /// </summary>
        /// <param name="f"></param>
        private void RefreshPanelWithFeed(Feed f)
        {
            m_EpisodeSource.DataSource = f.Episodes;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void IgnoreDataError(object sender, DataGridViewDataErrorEventArgs e)
        {
            e.ThrowException = false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="filename"></param>
        private void LoadXMLData(string filename)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(filename);

            foreach (XmlNode node in doc.ChildNodes)
            {
                if (node.Name == "prodcrast")
                {
                    foreach (XmlNode feedNode in node.ChildNodes)
                    {
                        if (feedNode.Name == "feed")
                        {
                            m_Feeds.Add(new Feed(feedNode));
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="filename"></param>
        private void SaveXMLData(string filename)
        {
            XmlDocument doc = new XmlDocument();

            XmlElement root = doc.CreateElement("prodcrast");
            foreach (Feed f in m_Feeds)
            {
                f.SaveToXML(doc, root);
            }

            doc.AppendChild(root);
            doc.Save(filename);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer1_Tick(object sender, EventArgs e)
        {
            for (int i = 0; i < m_FeedSource.List.Count; ++i)
            {
                Feed f = m_FeedSource.List[i] as Feed;
                if (f != null)
                {
                    if (f.NeedsRefresh)
                    {
                        f.NeedsRefresh = false;
                        m_FeedSource.ResetItem(i);
                    }
                }
            }

            for (int i = 0; i < m_EpisodeSource.List.Count; ++i)
            {
                Episode ep = m_EpisodeSource.List[i] as Episode;
                if (ep != null)
                {
                    if (ep.NeedsRefresh)
                    {
                        ep.NeedsRefresh = false;
                        m_EpisodeSource.ResetItem(i);
                    }
                    else if (ep.DownloadActive)
                    {
                        m_EpisodeSource.ResetItem(i);
                    }
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnScanAll_Click(object sender, EventArgs e)
        {
            ScanAllFeedsAsync();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dataGridFeeds_SelectionChanged(object sender, EventArgs e)
        {
            UpdateEpisodeList();
        }

        /// <summary>
        /// 
        /// </summary>
        private void UpdateEpisodeList()
        {
            Func<Episode, bool> filter =
                tabFilters.SelectedTab.Tag as Func<Episode, bool>;

            List<Episode> eps = new List<Episode>(
                m_Feeds.SelectMany(delegate(Feed f)
                {
                    return f.Episodes;
                }).Where(filter));
            eps.Sort(delegate(Episode l, Episode r) 
            { 
                return DateTime.Compare(r.Date, l.Date); 
            });

            m_EpisodeSource.DataSource = eps;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            SaveXMLData("prodcrast.xml");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dataGridFeeds_MouseClick(object sender, MouseEventArgs e)
        {
            Point mouse = e.Location;
            DataGridView.HitTestInfo hti = dataGridFeeds.HitTest(mouse.X, mouse.Y);

            if (e.Button == MouseButtons.Right)
            {
                if (hti.RowIndex >= 0 && hti.RowIndex <= m_Feeds.Count)
                {
                    Feed f = m_Feeds[hti.RowIndex];
                    contextFeeds.Tag = f;
                    contextFeeds.Show(dataGridFeeds, mouse); 
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void scanToolStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (DataGridViewRow feed in dataGridFeeds.SelectedRows)
            {
                new Thread((m_FeedSource[feed.Index] as Feed).Scan).Start();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dataGridEpisodes_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                Point mouse = e.Location;
                DataGridView.HitTestInfo hti = dataGridEpisodes.HitTest(mouse.X, mouse.Y);

                List<Episode> epsList = m_EpisodeSource.DataSource as List<Episode>;
                if (hti.RowIndex >= 0 && hti.RowIndex <= epsList.Count)
                {
                    Episode ep = epsList[hti.RowIndex];
                    contextEpisodes.Tag = ep;
                    contextEpisodes.Show(dataGridEpisodes, mouse);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void downloadToolStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (DataGridViewRow row in dataGridEpisodes.SelectedRows)
            {
                Episode ep = m_EpisodeSource[row.Index] as Episode;
                if(ep.State != Episode.EpisodeState.Listened)
                    ep.StartDownload();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnAddFeed_Click(object sender, EventArgs e)
        {
            FeedProperties fp = new FeedProperties();

            if (fp.ShowDialog() == DialogResult.OK)
            {
                Feed f = new Feed(fp.FeedURL);
                m_Feeds.Add(f);
                m_FeedSource.ResetBindings(false);// = m_Feeds;
                new Thread(f.Scan).Start();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void showInExplorerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (DataGridViewRow row in dataGridEpisodes.SelectedRows)
            {
                (m_EpisodeSource[row.Index] as Episode).ShowInExplorer();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ProdCrastMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            foreach(Feed f in m_Feeds)
            {
                f.AbortAllDownloads();
            }

            SaveXMLData("prodcrast.xml");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void markListenedToolStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (DataGridViewRow row in dataGridEpisodes.SelectedRows)
            {
                (m_EpisodeSource[row.Index] as Episode).State = Episode.EpisodeState.Listened;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void downloadAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            foreach (DataGridViewRow feed in dataGridFeeds.SelectedRows)
            {
                (m_FeedSource[feed.Index] as Feed).DownloadAll();
            }
        }

        #region System menu junk

        /// <summary>
        /// Add an "About Prodcrast..." item to the system menu.
        /// (liek when you click on the control box in the top left)
        /// 
        /// This whole bit is from
        /// http://www.codeproject.com/KB/dotnet/CustomWinFormSysMenu.aspx
        /// </summary>
        
        [System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
        private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
        [System.Runtime.InteropServices.DllImportAttribute("user32.dll")]
        private static extern bool InsertMenu(IntPtr hMenu, Int32 wPosition, Int32 wFlags, Int32 wIDNewItem,
             string lpNewItem);

        const int WM_SYSCOMMAND = 0x112;
        const int MF_SEPARATOR = 0x800;
        const int MF_BYPOSITION = 0x400;
        const int IDM_ABOUT = 1000;

        /// <summary>
        /// 
        /// </summary>
        private void SetupSystemMenu()
        {
            IntPtr sysMenuHandle = GetSystemMenu(this.Handle, false);

            int menuStart = 5;

            InsertMenu(sysMenuHandle, menuStart++, MF_BYPOSITION | MF_SEPARATOR, 0, string.Empty);
            InsertMenu(sysMenuHandle, menuStart++, MF_BYPOSITION, IDM_ABOUT, "About Prodcrast...");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="m"></param>
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == 0x112)
            {
                switch(m.WParam.ToInt32())
                {
                    case IDM_ABOUT:
                        AboutBox ab = new AboutBox();
                        ab.ShowDialog();
                        break;
                }
            }
            base.WndProc(ref m);
        }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tabFilters_Selecting(object sender, TabControlCancelEventArgs e)
        {
            e.TabPage.Controls.Add(dataGridEpisodes);

            UpdateEpisodeList();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dataGridFeeds_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                Point mouse = e.Location;
                DataGridView.HitTestInfo hti = dataGridEpisodes.HitTest(mouse.X, mouse.Y);

                if (hti.RowIndex >= dataGridFeeds.Rows.Count)
                {
                    foreach (DataGridViewRow r in dataGridFeeds.Rows)
                    {
                        r.Selected = false;
                    }
                }
            }
        }

        private void btnOptions_Click(object sender, EventArgs e)
        {
            SettingsForm sf = new SettingsForm();
            sf.ShowDialog();
        }
    }
}
